package com.googlecode.cswish.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.googlecode.cswish.struts.hibernate.IDynamicModel;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.OgnlExUtil;

/**
 * Populate the parameter in the flat structure, but it mocks child element to support the nested rule
 * 
 * @author Jerry.Feng
 */
public class ParameterMap extends HashMap<String, Object> {
	
	// false --> Condition.SEARCH_LIKEFIRST
	// true --> default value, it means advance search, Condition.ADVANCE_SEARCH
	public final static String COMPLEX_SEARCH = "_complexSearch";

	private static OgnlExUtil ognlUtil;
	
	private ParameterMap parent;
	private String parentName;
	private boolean disableAutoCreation;
	
	public ParameterMap() {
	}
	
	public ParameterMap(String parentName, ParameterMap parent) {
		this.parentName = parentName;
		this.parent = parent;
	}
	
	private OgnlExUtil getOgnlUtil() {
		if (ognlUtil == null) {
			ognlUtil = ObjectFactory.getInstance().getBean(OgnlExUtil.class, true);
		}
		return ognlUtil;
	}
	
	public boolean isDisableAutoCreation() {
		return disableAutoCreation;
	}

	public void setDisableAutoCreation(boolean disableAutoCreation) {
		this.disableAutoCreation = disableAutoCreation;
	}

	@Override
    public Object get(Object key) {
		if (parent != null) {
			return parent.get(parentName + '.' + key);
		}
		
		Object value = super.get(key);
		if (!disableAutoCreation && value == null) {
			// TODO: check the input parameter name to avoid out of memory
			// only create a temporarily object
			value = new ParameterMap(key.toString(), this);
			super.put((String)key, value);
		}
		return value;
	}
	
	@Override
	public Object put(String key, Object value) {
		if (parent == null) {
			return super.put(key, value);
		} else {
			// parent is not null, it's a temporary object, just put parameter to parent object
			return parent.put(parentName + '.' + key, value);
		}
	}
	
	public void setModelName(String modelName) {
		put(IDynamicModel.ENTITY_NAME, modelName);
	}
	
	public String getModelName() {
		Object name = get(IDynamicModel.ENTITY_NAME);
		if (name instanceof String) {
			return (String) name;
		} else {
			return null;
		}
	}
	
	public List<String> getParameterNames() {
		List<String> parameterNames = new ArrayList<String>();
		for (Entry<String, Object> entry : this.entrySet()) {
			if (!(entry.getValue() instanceof ParameterMap)) {
				parameterNames.add(entry.getKey());
			}
		}
		return parameterNames;
	}
	
	public void setParameter(String name, String value) {
		this.setParameter(name, new String[] {value});
	}
	
	public void setParameter(String name, String[] value) {
		int end = name.indexOf('.');
		if (end == -1) {
			this.put(name, value);
		} else {
			// create the temporary parent object
			int begin = 0;
			ParameterMap root = this;
			while (root.parent != null) {
				root = root.parent;
			}
			
			ParameterMap cur = root;
			while (end > 0) {
				String property = name.substring(begin, end);
				Object val = cur.get(property);
				if (!(val instanceof ParameterMap)) {
					val = new ParameterMap(property, cur);
					((ParameterMap)val).setDisableAutoCreation(disableAutoCreation);
					cur.put(property, val);
				}
				cur = (ParameterMap) val;
				
				begin = end + 1;
				end = name.indexOf('.', begin);
			}
			
			// set the parameter
			cur.put(name.substring(begin), value);
		}
	}
	
	public void setParameter(String name, Object value) {
		if (value instanceof String) {
			setParameter(name, (String)value);
		} else if (value instanceof String[]) {
			setParameter(name, (String[])value);
		} else if (value instanceof ParameterMap) {
			// skip this kind of parameter
		} else {
			String str = getOgnlUtil().convertValue(value, String.class, name);
			setParameter(name, str);
		}
	}
	
	/**
	 * 适用于遗留代码向高级查询的整合, 新的范围查询条件设置直接用表达式(注:前端应做相应的改造)
	 * 
	 * @param name
	 * @param value
	 */
	public void setRangeParameter(String name, String value) {
		String from;
		String to;
		// get current value, "" means null
		if (name.endsWith("2")) {
			name = name.substring(0, name.length() - 1);
			from = getParameter(name, String.class);
			if (from != null) {
				if (from.length() == 0) {
					from = null;
				} else {
					int index = from.indexOf('&');
					if (index > 0) {
						from = from.substring(0, index);
					}
				}
			}
			to = value;
			if (to != null && to.length() == 0) {
				to = null;
			}
		} else {
			from = value;
			if (from != null && from.length() == 0) {
				from = null;
			}
			to = getParameter(name + "2", String.class);
			if (to != null && to.length() == 0) {
				to = null;
			}
		}
		
		// format
		if (from != null && from.startsWith(">")) {
			from = from.substring(1);
		}
		if (to != null && to.startsWith("<")) {
			to = to.substring(1);
		}
		
		// combine
		if (from == null && to == null) {
			setParameter(name, "");
		} else if (to == null && from != null) {
			setParameter(name, '>' + from);
		} else if (from == null && to != null) {
			setParameter(name, '<' + to);
		} else {
			setParameter(name, '>' + from + "&<" + to);
		}
	}
	
	public String getParameter(String name) {
		return this.getParameter(name, String.class);
	}
	
	public <T> T getParameter(String name, Class<T> type) {
		Object val = get(name);
		if (!(val instanceof String[])) {
			return null;
		}
		String[] values = (String[])get(name);
		if (values == null) {
			return null;
		}
		
		// convert the common type firstly
		if (type.isArray()) {
			Class clazz = type.getClass().getComponentType();
			if (String.class.equals(clazz)) {
				return (T)values;
			} else {
				return (T) getOgnlUtil().getXWorkConverter().convertValue(values, type);
			}
		} else if (String.class.equals(type)) {
			if (values.length > 0) {
				return (T) values[0];
			}
		} else if (Integer.class.isAssignableFrom(type)) {
			if (values.length > 0) {
				return (T) new Integer(values[0]);
			}
		} else {
			return (T) getOgnlUtil().getXWorkConverter().convertValue(values, type);
		}
		return null;
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append('{');
		for (Map.Entry entry : this.entrySet()) {
			sb.append(entry.getKey()).append('=');
			if (entry.getValue() instanceof String[]) {
				String[] strs = (String[]) entry.getValue();
				if (strs.length > 0) {
					sb.append(strs[0]);
				}
			} else {
				sb.append(entry.getValue());
			}
			sb.append(',');
		}
		if (sb.length() > 1) {			
			sb.setCharAt(sb.length() - 1, '}');
		} else {
			sb.append('}');
		}
		return sb.toString();
	}
}